package clients

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/devops-smartbot/devops-smartbot/services/integration-service/internal/models"
	"go.uber.org/zap"
)

const pagerDutyAPIBase = "https://api.pagerduty.com"

type PagerDutyClient struct {
	config *models.PagerDutyConfig
	client *http.Client
	logger *zap.Logger
}

func NewPagerDutyClient(config *models.PagerDutyConfig, logger *zap.Logger) *PagerDutyClient {
	return &PagerDutyClient{
		config: config,
		client: &http.Client{
			Timeout: 30 * time.Second,
		},
		logger: logger,
	}
}

type pdIncidentRequest struct {
	Incident pdIncident `json:"incident"`
}

type pdIncident struct {
	Type             string              `json:"type"`
	Title            string              `json:"title"`
	Service          *pdReference        `json:"service,omitempty"`
	Urgency          string              `json:"urgency,omitempty"`
	Body             *pdIncidentBody     `json:"body,omitempty"`
	EscalationPolicy *pdReference        `json:"escalation_policy,omitempty"`
	Assignments      []pdAssignment      `json:"assignments,omitempty"`
}

type pdReference struct {
	ID   string `json:"id"`
	Type string `json:"type"`
}

type pdIncidentBody struct {
	Type    string `json:"type"`
	Details string `json:"details"`
}

type pdAssignment struct {
	Assignee pdReference `json:"assignee"`
}

type pdIncidentResponse struct {
	Incident pdIncidentDetail `json:"incident"`
}

type pdIncidentDetail struct {
	ID               string         `json:"id"`
	IncidentNumber   int            `json:"incident_number"`
	Title            string         `json:"title"`
	Description      string         `json:"description"`
	Status           string         `json:"status"`
	Urgency          string         `json:"urgency"`
	Service          pdReference    `json:"service"`
	EscalationPolicy pdReference    `json:"escalation_policy"`
	Assignments      []pdAssignment `json:"assignments"`
	CreatedAt        string         `json:"created_at"`
	UpdatedAt        string         `json:"updated_at"`
}

type pdIncidentUpdate struct {
	Incident pdIncidentUpdateFields `json:"incident"`
}

type pdIncidentUpdateFields struct {
	Type       string `json:"type"`
	Status     string `json:"status,omitempty"`
	Resolution string `json:"resolution,omitempty"`
}

// CreateIncident creates a new PagerDuty incident
func (c *PagerDutyClient) CreateIncident(title, description, urgency string) (*models.PagerDutyIncident, error) {
	url := fmt.Sprintf("%s/incidents", pagerDutyAPIBase)

	req := pdIncidentRequest{
		Incident: pdIncident{
			Type:  "incident",
			Title: title,
			Service: &pdReference{
				ID:   c.config.ServiceID,
				Type: "service_reference",
			},
			Urgency: urgency,
			Body: &pdIncidentBody{
				Type:    "incident_body",
				Details: description,
			},
		},
	}

	if c.config.EscalationPolicyID != "" {
		req.Incident.EscalationPolicy = &pdReference{
			ID:   c.config.EscalationPolicyID,
			Type: "escalation_policy_reference",
		}
	}

	jsonData, err := json.Marshal(req)
	if err != nil {
		return nil, fmt.Errorf("failed to marshal request: %w", err)
	}

	httpReq, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.Header.Set("Authorization", fmt.Sprintf("Token token=%s", c.config.APIKey))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Accept", "application/vnd.pagerduty+json;version=2")
	httpReq.Header.Set("From", "devops-smartbot@example.com")

	resp, err := c.client.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	if resp.StatusCode != http.StatusCreated {
		c.logger.Error("Failed to create PagerDuty incident",
			zap.Int("status", resp.StatusCode),
			zap.String("body", string(body)))
		return nil, fmt.Errorf("pagerduty API error: %d - %s", resp.StatusCode, string(body))
	}

	var pdResp pdIncidentResponse
	if err := json.Unmarshal(body, &pdResp); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	c.logger.Info("Created PagerDuty incident",
		zap.String("id", pdResp.Incident.ID),
		zap.Int("number", pdResp.Incident.IncidentNumber))

	return c.convertPDIncident(&pdResp.Incident), nil
}

// GetIncident retrieves a PagerDuty incident by ID
func (c *PagerDutyClient) GetIncident(incidentID string) (*models.PagerDutyIncident, error) {
	url := fmt.Sprintf("%s/incidents/%s", pagerDutyAPIBase, incidentID)

	httpReq, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.Header.Set("Authorization", fmt.Sprintf("Token token=%s", c.config.APIKey))
	httpReq.Header.Set("Accept", "application/vnd.pagerduty+json;version=2")

	resp, err := c.client.Do(httpReq)
	if err != nil {
		return nil, fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)

	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("pagerduty API error: %d - %s", resp.StatusCode, string(body))
	}

	var pdResp pdIncidentResponse
	if err := json.Unmarshal(body, &pdResp); err != nil {
		return nil, fmt.Errorf("failed to unmarshal response: %w", err)
	}

	return c.convertPDIncident(&pdResp.Incident), nil
}

// UpdateIncident updates a PagerDuty incident
func (c *PagerDutyClient) UpdateIncident(incidentID, status, resolution string) error {
	url := fmt.Sprintf("%s/incidents/%s", pagerDutyAPIBase, incidentID)

	updateReq := pdIncidentUpdate{
		Incident: pdIncidentUpdateFields{
			Type:   "incident_reference",
			Status: status,
		},
	}

	if resolution != "" {
		updateReq.Incident.Resolution = resolution
	}

	jsonData, err := json.Marshal(updateReq)
	if err != nil {
		return fmt.Errorf("failed to marshal request: %w", err)
	}

	httpReq, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonData))
	if err != nil {
		return fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.Header.Set("Authorization", fmt.Sprintf("Token token=%s", c.config.APIKey))
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("Accept", "application/vnd.pagerduty+json;version=2")
	httpReq.Header.Set("From", "devops-smartbot@example.com")

	resp, err := c.client.Do(httpReq)
	if err != nil {
		return fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("pagerduty API error: %d - %s", resp.StatusCode, string(body))
	}

	c.logger.Info("Updated PagerDuty incident", zap.String("id", incidentID))
	return nil
}

// TestConnection tests the PagerDuty connection
func (c *PagerDutyClient) TestConnection() error {
	url := fmt.Sprintf("%s/abilities", pagerDutyAPIBase)

	httpReq, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return fmt.Errorf("failed to create request: %w", err)
	}

	httpReq.Header.Set("Authorization", fmt.Sprintf("Token token=%s", c.config.APIKey))
	httpReq.Header.Set("Accept", "application/vnd.pagerduty+json;version=2")

	resp, err := c.client.Do(httpReq)
	if err != nil {
		return fmt.Errorf("failed to send request: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("pagerduty connection test failed: %d - %s", resp.StatusCode, string(body))
	}

	c.logger.Info("PagerDuty connection test successful")
	return nil
}

func (c *PagerDutyClient) convertPDIncident(pdIncident *pdIncidentDetail) *models.PagerDutyIncident {
	incident := &models.PagerDutyIncident{
		ID:                 pdIncident.ID,
		IncidentNumber:     fmt.Sprintf("%d", pdIncident.IncidentNumber),
		Title:              pdIncident.Title,
		Description:        pdIncident.Description,
		Status:             pdIncident.Status,
		Urgency:            pdIncident.Urgency,
		ServiceID:          pdIncident.Service.ID,
		EscalationPolicyID: pdIncident.EscalationPolicy.ID,
		AssigneeIDs:        []string{},
	}

	for _, assignment := range pdIncident.Assignments {
		incident.AssigneeIDs = append(incident.AssigneeIDs, assignment.Assignee.ID)
	}

	if createdAt, err := time.Parse(time.RFC3339, pdIncident.CreatedAt); err == nil {
		incident.CreatedAt = createdAt
	}

	if updatedAt, err := time.Parse(time.RFC3339, pdIncident.UpdatedAt); err == nil {
		incident.UpdatedAt = updatedAt
	}

	return incident
}
